#include "jot.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <boost/algorithm/string.hpp>
#include <boost/lambda/lambda.hpp>
#include <gtksourceviewmm/sourcelanguagesmanager.h>
#include <gtksourceviewmm/init.h>
#include <gtksourceviewmm/sourceview.h>

namespace jot {

// Document class

Document::Document(const std::string& filename, const Glib::RefPtr<gtksourceview::SourceBuffer>& refSourceBuffer) :
	m_filename(filename), m_refSourceBuffer(refSourceBuffer) {
}

std::string Document::get_filename() {
	return m_filename;
}

std::string Document::get_text() {
	return m_refSourceBuffer->get_text();
}

void Document::set_filename(const std::string& filename) {
	m_filename = filename;
}

void Document::write_file() {
	std::ofstream file(m_filename.c_str());
	if (file.is_open()) {
		file << m_refSourceBuffer->get_text();
		file.close();
		m_refSourceBuffer->set_modified(false);
	} else {
		std::cout << "Unable to open file"<< std::endl;
	}
}

// end Document class

// Window class

Window::Window() {

	gtksourceview::init();

	set_title("Jot");
	set_default_size(750, 900);

	Glib::RefPtr<Gtk::ActionGroup> refActionGroup = Gtk::ActionGroup::create();
	refActionGroup->add(Gtk::Action::create("FileMenu", "File"));
	refActionGroup->add(Gtk::Action::create("FileNew", Gtk::Stock::NEW, "_New", "Create a new document"), sigc::mem_fun(*this, &Window::on_menu_file_new));
	refActionGroup->add(Gtk::Action::create("FileOpen", Gtk::Stock::OPEN, "_Open", "Open a file"), sigc::mem_fun(*this, &Window::on_menu_file_open));
	refActionGroup->add(Gtk::Action::create("FileSave", Gtk::Stock::SAVE, "_Save", "Save the current file"), sigc::mem_fun(*this, &Window::on_menu_file_save));
	refActionGroup->add(Gtk::Action::create("FileSaveAs", Gtk::Stock::SAVE_AS, "Save _As...", "Save the current file with a different name"), sigc::mem_fun(
			*this, &Window::on_menu_file_save_as));
	refActionGroup->add(Gtk::Action::create("FileClose", Gtk::Stock::CLOSE, "_Close", "Close document"), sigc::mem_fun(*this, &Window::on_menu_file_close));
	refActionGroup->add(Gtk::Action::create("FileQuit", Gtk::Stock::QUIT), sigc::mem_fun(*this, &Window::on_menu_file_quit));

	Glib::RefPtr<Gtk::UIManager> refUIManager = Gtk::UIManager::create();
	refUIManager->insert_action_group(refActionGroup);
	refUIManager->add_ui_from_string("<ui>"
		"  <menubar name='MenuBar'>"
		"    <menu action='FileMenu'>"
		"      <menuitem action='FileNew'/>"
		"      <menuitem action='FileOpen'/>"
		"      <separator/>"
		"      <menuitem action='FileSave'/>"
		"      <menuitem action='FileSaveAs'/>"
		"      <separator/>"
		"      <menuitem action='FileClose'/>"
		"      <menuitem action='FileQuit'/>"
		"    </menu>"
		"  </menubar>"
		"</ui>");
	add_accel_group(refUIManager->get_accel_group());

	append_page();

	Gtk::VBox* pVBox = manage(new Gtk::VBox());
	pVBox->pack_start(*refUIManager->get_widget("/MenuBar"), Gtk::PACK_SHRINK);
	pVBox->pack_start(m_Notebook, Gtk::PACK_EXPAND_WIDGET);
	add(*pVBox);
	show_all_children();
}

void Window::append_page(const std::string& child_text, const std::string& filename) {
	Glib::RefPtr<gtksourceview::SourceLanguagesManager> refSourceLanguagesManager = gtksourceview::SourceLanguagesManager::create();
	Glib::ustring mime_type("text/xml");	
	Glib::RefPtr<gtksourceview::SourceLanguage> refSourceLanguage = refSourceLanguagesManager->get_language_from_mime_type(mime_type);
	Glib::RefPtr<gtksourceview::SourceBuffer> refSourceBuffer = gtksourceview::SourceBuffer::create(refSourceLanguage);

	refSourceBuffer->set_highlight();
	refSourceBuffer->set_text(child_text);
	refSourceBuffer->place_cursor(refSourceBuffer->begin());

	DocumentPtr pDocument(new Document(filename, refSourceBuffer));
	m_pDocuments.push_back(pDocument);

	gtksourceview::SourceView* pSourceView = manage(new gtksourceview::SourceView(refSourceBuffer));
	pSourceView->set_show_line_numbers();
	pSourceView->set_highlight_current_line();
	pSourceView->set_margin(120);
	pSourceView->set_show_margin();
	Gtk::ScrolledWindow* pScrolledWindow = manage(new Gtk::ScrolledWindow());
	pScrolledWindow->add(*pSourceView);
	pScrolledWindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);

	int position = m_Notebook.append_page(*pScrolledWindow, tab_label(filename));
	m_Notebook.show_all_children();
	m_Notebook.set_current_page(position);
}

void Window::read_file(const std::string& filename) {
	std::ifstream file(filename.c_str());
	if (file.is_open()) {
		std::stringstream ss;
		ss << file.rdbuf();
		file.close();
		std::string contents = ss.str();
		append_page(contents, filename);
	} else {
		std::cout << "Unable to open file"<< std::endl;
	}
}

void Window::write_file(const std::string& filename) {
	int currentPage = m_Notebook.get_current_page();
	m_Notebook.pages()[currentPage].set_tab_label_text(tab_label(filename));
	DocumentPtr pDocument(m_pDocuments[currentPage]);
	pDocument->set_filename(filename);
	pDocument->write_file();
}

void Window::openFileChooserDialog(const std::string& title, Gtk::FileChooserAction action, const Gtk::StockID& ok_stock_id,
		const sigc::slot<void, const std::string&>& callback) {
	Gtk::FileChooserDialog dialog(title, action);
	dialog.set_transient_for(*this);
	dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
	dialog.add_button(ok_stock_id, Gtk::RESPONSE_OK);
	if (Gtk::RESPONSE_OK == dialog.run()) {
		callback(dialog.get_filename());
	}
}

void Window::on_menu_file_new() {
	append_page();
}

void Window::on_menu_file_open() {
	openFileChooserDialog("File::Open", Gtk::FILE_CHOOSER_ACTION_OPEN, Gtk::Stock::OPEN, sigc::mem_fun(*this, &Window::read_file));
}

void Window::on_menu_file_save() {
	DocumentPtr pDocument(m_pDocuments[m_Notebook.get_current_page()]);
	std::string filename(pDocument->get_filename());
	if (0 < filename.length()) {
		pDocument->write_file();
	} else {
		on_menu_file_save_as();
	}
}

void Window::on_menu_file_save_as() {
	openFileChooserDialog("File::Save as", Gtk::FILE_CHOOSER_ACTION_SAVE, Gtk::Stock::SAVE, sigc::mem_fun(*this, &Window::write_file));
}

void Window::on_menu_file_close() {
	m_Notebook.remove_page(m_Notebook.get_current_page());
}

void Window::on_menu_file_quit() {
	hide();
}

// end Window class

// functions

std::string tab_label(const std::string& filename) {
	std::string tab_label;
	if ( 0 < filename.length() ) {
		std::vector<std::string> splits;
		boost::split(splits, filename, boost::is_any_of("/") );
		tab_label = splits.back();
	} else {
		tab_label = "Untitled";
	}
	return tab_label;
}

// end functions

} // end namespace jot
